﻿using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;

namespace TfsUtils.Ui.Mvvm.Command
{
	[MarkupExtensionReturnType( typeof( DelegateCommand ) )]
	public class BindCommand : MarkupExtension
	{
		public BindCommand()
		{
		}

		public BindCommand( string commandName )
		{
			this.Name = commandName;
		}

		public string Name { get; set; }

		public override object ProvideValue( IServiceProvider serviceProvider )
		{
			IProvideValueTarget pvt = serviceProvider.GetService( typeof( IProvideValueTarget ) ) as IProvideValueTarget;
			if( pvt != null )
			{
				var fe = pvt.TargetObject as FrameworkElement;
				if( fe != null )
				{
					fe.Loaded += new RoutedEventHandler( HandleFrameworkElementLoaded );
				}
			}
			return null;
		}

		void OnCommandExecuted( object target, ExecutedRoutedEventArgs e )
		{

		}


		private void HandleFrameworkElementLoaded( object sender, RoutedEventArgs e )
		{
			var fe = ( sender as FrameworkElement );
			if( DesignerProperties.GetIsInDesignMode( fe ) ) return;

			if( fe.DataContext == null ) return;
			//Use reflection to retrieve provided Command
			PropertyInfo property = fe.DataContext.GetType().GetProperty( this.Name );
			//Get root elements
			FrameworkElement rootElement = this.GetRootElement( fe );
			var command = ( DelegateCommand )property.GetValue( fe.DataContext, null );
			ICommandSource source = sender as ICommandSource;
			if( source != null )
			{
				this.OnCommandBind( fe, command );

				//Add commandbindings to root element
				if( command.InputBindings != null )
				{
					foreach( InputBinding ib in command.InputBindings )
					{
						if( ib.CommandParameter != source.CommandParameter )
						{
							ib.CommandParameter = source.CommandParameter;
						}

						rootElement.InputBindings.Add( ib );
					}
				}
			}
			//De-register loaded event
			fe.Loaded -= HandleFrameworkElementLoaded;
		}

		protected virtual void OnCommandBind( FrameworkElement fe, DelegateCommand command )
		{
			if( fe is Button )
			{
				Button button = (Button)fe;
				button.Command = command;

				if( !String.IsNullOrEmpty( command.DisplayText ) && button.Content == null )
				{
					button.Content = new AccessText() { Text = command.DisplayText };
				}
			}
			else if( fe is MenuItem )
			{
				//MenuItem
				MenuItem menuItem = (MenuItem)fe;
				menuItem.Command = command;

				if( !String.IsNullOrEmpty( command.DisplayText ) )
				{
					menuItem.Header = new AccessText() { Text = command.DisplayText };
				}

				if( command.InputBindings != null )
				{
					KeyGesture keyGesture = command.InputBindings[ 0 ].Gesture as KeyGesture;
					menuItem.InputGestureText = keyGesture.DisplayString;
				}
			}
		}

		private FrameworkElement GetRootElement( FrameworkElement fe )
		{
			if( fe.Parent == null )
				return fe;
			else
			{
				return this.GetRootElement( fe.Parent as FrameworkElement );
			}
		}
	}
}
